package org.jederik.studip.android.provider;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Intent;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;

import org.jederik.android.provider.StudIP;
import org.jederik.android.provider.StudIP.*;
import org.jederik.android.provider.StudIP.Courses.Participants;
import org.jederik.studip.android.Const;
import org.jederik.studip.android.conn.RESTClient;
import org.jederik.studip.android.view.Preferences;

/**
 * Dieser Content Provider bietet den Zugriff auf die Nutzerdaten. Er ist unter
 * der URI {@code content://org.jederik.android.provider.StudIP} registriert.
 * Die Tabellen und ihre Spalten sind in {@link org.jederik.android.provider.StudIP}
 * dokumentiert.
 * @author Jann-Frederik Laß
 *
 */
public class StudipProvider extends ContentProvider {

	private static final String TAG = "ServiceProvider";

	
	// Helfer zum aufschlüsseln der URIs:
    private static final UriMatcher sUriMatcher = new StudipUriMatcher();

    // Helfer für den Zugriff auf die Datenbank:
    private DatabaseHelper mDatabaseHelper;
    
    // Helfer für die Kommunikation mit dem Webservice:
    private RESTClient mRESTClient;
    
	
	@Override
	public boolean onCreate() {
		mDatabaseHelper = new DatabaseHelper(getContext());
		mRESTClient = new RESTClient(getContext());
		return true;
	}

	@Override
	public String getType(Uri uri) {
        switch (sUriMatcher.match(uri)) {
        case StudipUriMatcher._ALL:					return StudIP.CONTENT_TYPE;
        case StudipUriMatcher.FEED:						return StudIP.Feed.CONTENT_TYPE;
        case StudipUriMatcher.FEED_ID:					return StudIP.Feed.CONTENT_ITEM_TYPE;
        case StudipUriMatcher.NEWS_ID:					return StudIP.News.CONTENT_ITEM_TYPE;
        case StudipUriMatcher.MESSAGES_ID:				return StudIP.Messages.CONTENT_ITEM_TYPE;
        case StudipUriMatcher.MESSAGES:					return StudIP.Messages.CONTENT_TYPE;
        case StudipUriMatcher.SCHEDULES:				return StudIP.Schedules.CONTENT_TYPE;
        case StudipUriMatcher.SCHEDULES_ID:				return StudIP.Schedules.CONTENT_ITEM_TYPE;
        case StudipUriMatcher.MYCOURSES:				return StudIP.Mycourses.CONTENT_TYPE;
        case StudipUriMatcher.COURSES_ID:				return StudIP.Courses.CONTENT_ITEM_TYPE;
        case StudipUriMatcher.COURSES_ID_DETAILS:		return StudIP.Courses.Details.CONTENT_ITEM_TYPE;
        case StudipUriMatcher.COURSES_ID_PARTICIPANTS:	return StudIP.Courses.Participants.CONTENT_TYPE;
        case StudipUriMatcher.MESSAGES_SENT:			return StudIP.MessagesSent.CONTENT_TYPE;
        case StudipUriMatcher.MESSAGES_SENT_ID:			return StudIP.MessagesSent.CONTENT_ITEM_TYPE;
        default:
        	throw new IllegalArgumentException("Unknown URI " + uri);
        }
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {

		// Schreibzugriff auf Datenbank anfordern:
		SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();

		// URI aufschlüsseln:
		switch (sUriMatcher.match(uri)) {
		
		case StudipUriMatcher.MESSAGES_SENT:
			
			// Nachricht in lokale DB eintragen:
			values.put(StudIP._SYNC_STATE, StudIP.STATE_PENDING);
			long id = db.insert(MessagesSent.DB_TABLE_NAME, null, values);
			
			// mit Server synchronisieren:
			Intent sync = new Intent(
					Intent.ACTION_SYNC, 
					Uri.withAppendedPath(uri, ""+id));
			getContext().startService(sync);
			
		default:
			return null;
		}
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
			String sortOrder) {

		// Lesezugriff auf Datenbank anfordern:
		SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
		
		// Objekt-ID:
		String id;

		// URI aufschlüüseln:
		switch (sUriMatcher.match(uri)) {
		case StudipUriMatcher.FEED:
			
			// Liste der Feed-Einträge zurückliefern:
			return db.query(
					org.jederik.android.provider.StudIP.Feed.DB_TABLE_NAME, 
					null, null, null, null, null, 
					Feed.DATE + " DESC");
			
		case StudipUriMatcher.NOTIFICATIONS:

			// Es werden nur Benachrichtigungen über Änderungen zurückgeliefert,
			// die seit dem letzten Betrachten der Feed-Ansicht dazugekommen sind:
			int lastFeedView = getContext().getSharedPreferences(Const.PREFS_NAME, 0)
			.getInt(Preferences.LAST_FEED_VIEW, -1);

			// Änderungen werden nach Typ gruppiert und die jeweilige Anzahl zurückgegeben:
			return db.query(
					org.jederik.android.provider.StudIP.Feed.DB_TABLE_NAME, 
					new String[] {
							Feed.OBJECT_TYPE + " AS " + Notifications.TYPE, 
							"count(*) AS " + Notifications.COUNT
					}, 
					"date>" + lastFeedView, 
					null, 
					"type", 
					null, 
					null);

		case StudipUriMatcher.MESSAGES_ID:
			
			// ID aus URI auslesen:
			id = uri.getLastPathSegment();
			
			// Objekt zurückliefern:
			return queryForResource(Messages.DB_TABLE_NAME, id, uri);
			
		case StudipUriMatcher.NEWS_ID:
			
			// ID aus URI auslesen:
			id = uri.getLastPathSegment();
			
			// Objekt zurückliefern:
			return queryForResource(News.DB_TABLE_NAME, id, uri);

		case StudipUriMatcher.SCHEDULES_ID:
			
			// ID aus URI auslesen:
			id = uri.getLastPathSegment();
			
			// Objekt zurückliefern:
			return queryForResource(Schedules.DB_TABLE_NAME, id, uri);

		case StudipUriMatcher.MYCOURSES:
			
			// Liste der besuchten Veranstaltungen zurückliefern:
			return db.query(
					Mycourses.DB_TABLE_NAME, 
					null, null, null, null, null, null);
			
		case StudipUriMatcher.COURSES_ID_DETAILS:
			
			// ID aus URI auslesen:
			id = uri.getPathSegments().get(1);
			
			// Objekt zurückliefern:
			return queryForResource(CourseDetails.DB_TABLE_NAME, id, uri);

		case StudipUriMatcher.COURSES_ID_PARTICIPANTS:
			
			// ID aus URI auslesen:
			String courseId = uri.getPathSegments().get(1);
			
			// Objekt zurückliefern:
			return mRESTClient.cursorGet(Courses.WS_PATH + "/" + courseId + Participants.WS_PATH);

		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}

	}

	/*
	 * Hilfsmethode zum lesen einer einzelnen Resource aus einer Datenbanktabelle
	 */
	private Cursor queryForResource(String table, String objectId, Uri uri) {

		// Schreibzugriff auf Datenbank anfordern:
		SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
		
		Cursor result = null;
		
		// WHERE-Phrase für Datenbankabfrage vorbereiten:
		String whereClause = StudIP.OBJECT_ID + "='" + objectId + "'";

		// Cursor aus Datenbank holen:
		result = db.query(
				table, 
				null, 
				whereClause, 
				null, null, null, null);
		
		// Nicht gefunden?
		if (result.getCount() == 0) {
			
			// Platzhalter mit Status PENDING einfügen:
			ContentValues proxy = new ContentValues();
			proxy.put(StudIP.OBJECT_ID, objectId);
			proxy.put(StudIP._SYNC_STATE, StudIP.STATE_PENDING);
			db.insert(table, null, proxy);
			
			// Neu laden:
			result.requery();
			
		}
		
		// Gefunden?
		if ( result.moveToFirst()) {
			
			// Synchronisationsstatus auslesen:
			int syncState = result.getInt(result.getColumnIndex(StudIP._SYNC_STATE));

			// Synchronisation ausstehend?
			if (syncState == StudIP.STATE_PENDING) {

				// Falls Synchronisation aussteht, diese starten:
				Intent i = new Intent(Intent.ACTION_SYNC, uri);
				getContext().startService(i);
			}
			
		}

		// Cursor wieder auf Element zurücksetzen:
		result.moveToPrevious();
		
		return result;
	}
	
	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		
		// Schreibzugriff auf Datenbank anfordern:
		SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
		
		int affectedRows;
		
		// URI aufschlüsseln:
		switch (sUriMatcher.match(uri)) {
		
		case StudipUriMatcher.FEED_ID:
			
			// ID des Objektes, auf das sich die Änderung bezieht, auslesen:
			String id = uri.getLastPathSegment();
			
			// WHERE-Phrase für DB-Abfrage vorbereiten:
			String whereClause = StudIP.OBJECT_ID + "='" + id + "'";
			
			// Änderung in Datenbank eintragen:
			affectedRows = db.update(Feed.DB_TABLE_NAME, values, whereClause, null);

			// Synchronisation angefordert?
			if (values.getAsInteger(StudIP._SYNC_STATE) == StudIP.STATE_PENDING) {

				// Synchronisation bewirken:
				Intent sync = new Intent(
						Intent.ACTION_SYNC, 
						uri
				);
				getContext().startService(sync);

			}

			break;
			
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
		
		return affectedRows;
	}

	@Override
	public int delete(Uri arg0, String arg1, String[] arg2) {
		return 0;
	}
}
